#include <iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<ctype.h>
#include <sys/stat.h>
#include <fcntl.h>
using namespace std;
const int basesize = 1024;
const int argvnum = 64;
const int envnum = 64;
char *gargv[argvnum];
int gargc = 0;
char pwd[basesize];
char pwdenv[basesize];
char *genv[envnum];
int lastcode = 0;

#define NoneRedir 0
#define InputRedir 1
#define OutputRedir 2
#define AppRedir 3
int redir = NoneRedir;
char* filename = nullptr;
#define TrimSpace(pos) do{\
  while(isspace(*pos)){\
      pos++;\
  }\
}while(0)
string GetUserName()
{
  string name = getenv("USER");
  return name.empty() ? "None" : name;
}
string GetHostName()
{
  string hostname = getenv("HOSTNAME");
  return hostname.empty() ? "None" : hostname;
}
string GetPwd()
{
  if(nullptr == getcwd(pwd,sizeof(pwd))) return "None";
  snprintf(pwdenv,sizeof(pwdenv),"PWD=%s",pwd);
  putenv(pwdenv);
  return pwd;
  //string pwd = getenv("PWD");
  //return pwd.empty() ? "None" : pwd;
}
string LastDir()
{
  string curr = GetPwd();
  if(curr == "/" || curr == "None")return curr;
  size_t pos = curr.rfind("/");
  if(pos == string::npos)return curr;
  return curr.substr(pos+1);
}
string MakeCommandLine()
{
  char command_line[basesize];
  snprintf(command_line,basesize,"[%s@%s %s]# ",\
      GetUserName().c_str(),GetHostName().c_str(),LastDir().c_str());
  return command_line;
}
void PrintCommandLine()
{
  printf("%s",MakeCommandLine().c_str());
  fflush(stdout);
}
bool GetCommandLine(char command_buffer[],int size)
{
  char* result = fgets(command_buffer,size,stdin);
  if(result == nullptr)
    return false;
  command_buffer[strlen(command_buffer)-1] = 0;
  if(strlen(command_buffer) == 0)return false;
  return true;
}
void ResetCommandLine()
{
  memset(gargv,0,sizeof(gargv));
  gargc = 0;
  redir = NoneRedir;
  filename = nullptr;
}
void ParseRedir(char command_buffer[],int len)
{
  int end = len - 1;
  while(end >= 0)
  {
    if(command_buffer[end] == '<')
    {
      redir = InputRedir;
      command_buffer[end] = 0;
      filename = &command_buffer[end]+1;
       TrimSpace(filename);
      //filename = &command_buffer[end+1];
      break;
    }
    else if(command_buffer[end] == '>')
    {
     if(command_buffer[end-1] == '>')
     {
       redir = AppRedir;
       command_buffer[end] = 0;
       command_buffer[end-1] = 0;
       filename = &command_buffer[end+1];
       TrimSpace(filename);
       break;
     }
     else 
     {
        redir = OutputRedir;
        command_buffer[end] = 0;
        filename = &command_buffer[end] + 1;
        TrimSpace(filename);
        break;
     }
    }
    else 
    {
      end--;
    }
  }
}
void ParseCommand(char command_buffer[])
{
  const char *sep = " ";
  gargv[gargc++] = strtok(command_buffer,sep);
  while((bool)(gargv[gargc++] = strtok(nullptr,sep)));//切割所有字符串
  gargc--;
}
void ParseCommandLine(char command_buffer[],int len)
{
  ResetCommandLine();
  ParseRedir(command_buffer,len);
  ParseCommand(command_buffer);
   //printf("command start: %s\n",command_buffer); 
   //printf("redir: %d\n",redir);
   //printf("filename: %s\n",filename);
   //printf("command end: %s\n",command_buffer);
}
void debug()
{
  printf("argc: %d\n",gargc);
  for(int i = 0; gargv[i];i++)
  {
    printf("argv[%d]: %s\n",i,gargv[i]);
  }

}
void DoRedir()
{
 if(redir == InputRedir)
 {
   if(filename)
   {
     int fd = open(filename,O_RDONLY);
     
        if(fd<0)
          exit(2);//打开文件失败或者没有输入
     dup2(fd,0);
   }
   else 
   {
     exit(1);//文件指向为空，文件不存在
   }

 }
 else if(redir == OutputRedir)
 {
   if(filename)
   {
     int fd = open(filename,O_CREAT | O_WRONLY | O_TRUNC,0666);
     if(fd < 0)
       exit(4);
     dup2(fd,1);
   }
   else 
   {
     exit(1);
   }

 }
 else if(redir == AppRedir)
 {
   if(filename)
   {
     int fd = open(filename,O_CREAT | O_WRONLY | O_APPEND,0666);
     if(fd < 0)
       exit(6);
     dup2(fd,1);
   }
   else 
     exit(5);
 }
 else 
 {
   //
 }
}
bool ExecuteCommandLine()//执行命令
{
  pid_t id = fork();
  if(id < 0) return false;
  if(id == 0)
  {
    //1.执行命令
    DoRedir();
    //execvp(gargv[0],gargv);
  
    execvpe(gargv[0],gargv,genv);
    //2.退出
    exit(7);
  }
  int status = 0;
  pid_t rid = waitpid(id,&status,0);
  if(rid > 0)
  {
    if(WIFEXITED(status))
    {
      lastcode = WEXITSTATUS(status);
    }
    else 
    {
      lastcode = 100;
    }
    return true;
  }
  return false;
}
void InitEnv()
{                                                                                                                        
  extern char **environ;
  int index = 0;
  while(environ[index])
  {
    genv[index] = (char*)malloc(strlen(environ[index])+1);
    strncpy(genv[index],environ[index],strlen(environ[index])+1);
    ++index;
   }
   genv[index]= nullptr;
  
}

void AddEnv(const char* item)
{
     int index = 0;
     while(genv[index])                                                                                                  
     {
       index++;
     }
     genv[index] = (char*)malloc(strlen(item)+1);
     strncpy(genv[index],item,strlen(item)+1);
     genv[++index] = nullptr;
}

bool CheckAndExecBuiltCommand()
{
  if(strcmp(gargv[0],"cd") == 0)
  {
    if(gargc == 2)
    {
      chdir(gargv[1]);
    }
    else 
    {
      lastcode = 1;
    }
    return true;
  }
  else if(strcmp(gargv[0],"export") == 0)
  {
    if(gargc == 2)
    {
      AddEnv(gargv[1]);
    }
    else 
    {
      lastcode = 2;
    }
    return true;
  }
  else if(strcmp(gargv[0],"env") == 0)
  {
    for(int i = 0; genv[i];i++)
    {
      printf("%s\n",genv[i]);
    }
    lastcode = 0;
    return true;
  }
  else if(strcmp(gargv[0],"echo") == 0)
  {
    if(gargc == 2)
    {
      if(gargv[1][0] == '$')
      {
         if(gargv[1][1] == '?')
         {
           printf("%d\n ",lastcode);
           lastcode = 0;
         }
      }
      else 
      {
        printf("%s\n",gargv[1]);
      }
    }
    else 
    {
      lastcode = 3;
    }
    return true;
  }
  return false;

}
//void InitEnv()
//{
//  extern char **environ;
//  int index = 0;
//  while(environ[index])
//  {
//    genv[index] = (char*)malloc(strlen(environ[index])+1);
//    strncpy(genv[index],environ[index],strlen(environ[index])+1);
//    ++index;
//  }
//  genv[index]= nullptr;
//  
//}
//void AddEnv(const char* item)
//{
//  int index = 0;
//  while(gargv[index])
//  {
//    index++;
//  }
//  genv[index] = (char*)malloc(strlen(item)+1);
//  strncpy(genv[index],item,strlen(item)+1);
//  genv[++index] = nullptr;
//}
int main()
{
  InitEnv();
  char command_buffer[basesize];
  while(true)
  {
     PrintCommandLine();//打印命令行提示符
     if(!GetCommandLine(command_buffer,basesize))//获取用户命令
     {
       continue;
     }
     //printf("%s\n", command_buffer);
     ParseCommandLine(command_buffer,strlen(command_buffer));//分析命令
     //debug();

     if(CheckAndExecBuiltCommand())
     {
       continue;
     }

     ExecuteCommandLine();//执行命令
  }
  return 0;
}
